/**
 * 我们的网页应用程序通常需要发送请求用于报告事件，更新状态和数据分析。
 * 这样的请求往往不需要服务器响应，（返回一个空响应体）
 * 并且不应该造成网络和计算资源的竞争。
 * 同时这样的请求一般用于交付关键应用和测试数据，这就迫使开发者使用昂贵的方法（合并延迟提交、同步提交）确保分发。
 * 
 * ·开发者更倾向于选择立即提交的请求，而不是合并与延迟的提交，因为可以提高分发效率。
 *  延迟提交的请求可能没有充足的时间完成，这回造成重要数据的丢失。
 * ·使用同步提交或者无业务繁忙循环会在时间临界（click、unload等)操作中阻止用户代理（浏览器），损害用户体验。
 *  并且这样的阻塞行为在页面卸载关闭时，会被系统取消、挂起或者杀死。
 * 
 * beacon 规范定义了一个可以通过异步非阻塞方式发送数据，并且在时间临界操作（click、unload等）中最小化连接所需资的接口。
 * 来保证请求会被处理和发送到目标服务器。
 * 
 * · beacon 请求优先避免时间临界操作和高优先级的网络请求之间的冲突和竞争。
 * · beacon 请求被用户代理（浏览器）高效合并，用于优化在移动端的表现。
 * · beacon 请求可以保证在页面卸载前完成初始化，也保证无需阻塞请求和其他阻塞用户交互的技术也能完成。
 * 
 * beacon 规范定义了一个函数 sendBeacon 挂载在 navigator 对象上，在 Worker 中，则挂载在 WorkerNavigator 对象上
 * 值得注意的是，sendBeason(url, data) 中 data 只支持 ArrayBufferView、Blob、DOMString、FormData
 * 当 data 是 Blob 类型时，需要 DNS 或者服务器端设置跨域，这是出于安全考虑，目前该特性的兼容性还很差
 * 
 * sendBeacon 返回一个 Boolean 值，表示发送的数据量是否超过用户代理（浏览器）的显示。
 *    true: 表明没有超过，且已经将数据排队等待
 *    false: 表示要排队的数据已经超过用户代理的限制
 * 
 * 下面是一个例子：
 */
const url = 'http://localhost:3000/users/webpai/beaconpost';

function reportEvent(event) {
  let data = JSON.stringify({
    event: event,
    time: performance.now()
  });
  sendBeacon(url, data)
}

document.addEventListener('visibilitychange', () => {
  if(document.visibilityState === 'hidden') {
    sendBeacon(url, JSON.stringify({
      event: 'visibility change',
      time: performance.now()
    }))
  }
})

/**
 * 封装sendBeacon接口，检查兼容性和参数
 * @param {string} url 请求地址
 * @param {Object} data 发送数据
 */
function sendBeacon (url, data) {
  if(!url) throw new Error('缺少url');
  data = data || {};
  if(navigator.sendBeacon) {
    if(!navigator.sendBeacon(url, data)) {
      alert('发送的数据超出限制');
    }
  } else {
    alert('您的浏览器不支持 Beacon');
    return false;
  }
}